// Copyright 2009 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: Jeff Stewart
//
// Defines the interface to a Probe and related auxiliary structures.
// A probe has a name (used for reporting and support hyperlinking only).
// A probe is put in a ProbeSet, and the Executive runs the probes one at a
// time by calling Start().  Each derived probe should implement StartProbe()
// which will be the entry point into that probes execution.
//
// When a probe is complete, it should emit the ProbeComplete() signal.
// This base class will do some housekeeping and then emit the Complete()
// signal.
//
// As the probe executes, it should regularly emit ProgressMade() signals
// so that the UI can give an accurate view of the progress of the test.
//
// GetDisplayText() must render a locale-appropriate presentation of the
// faults encountered, if any.  Use QObject::tr() for such translations.
//
// GetFaultId() must return a probe-scoped integer defining the fault
// encountered.  These Ids are passed to ui/support.h methods to map a fault
// incidence to a support page on the web.
//
// DebugText() outputs a blob of HTML to be included in the report.

#ifndef POPDIAG_PROBES_PROBE_H__
#define POPDIAG_PROBES_PROBE_H__

#include <QtCore/QList>
#include <QtCore/QMutex>
#include <QtCore/QObject>
#include <QtCore/QPair>
#include <QtCore/QString>
#include <QtCore/QTextStream>

#include "util/common.h"

namespace popdiag {


typedef enum { NOT_EXECUTED, RUNNING, FINISHED } ExecuteStatus;
typedef enum { PROBE_OK, PROBE_FAILED } ResultStatus;

// A log that writes to a QString.
class ToStringLog : public QTextStream {
 public:
  ToStringLog();

  void Open() { is_open_ = true; }
  void Close() { is_open_ = false; }
  bool IsOpen() const { return is_open_; }
  QString GetText() const { return text_; }
  void Clear() { is_open_ = false; text_.clear(); }
 private:
  QString text_;
  bool    is_open_;
};

class Probe : public QObject {
  Q_OBJECT
 public:
  // name should be the untranslated result.
  explicit Probe(const QString &name);
  virtual ~Probe();

  // A non-localized name for this probe.
  const QString &name() const { return name_; }

 signals:
  // Progress has been made
  void ProgressMade(int done, int total);

  // This signal should be emitted by derived classes when
  // they complete.  It should be emitted exactly once.
  void ProbeComplete(ResultStatus r);

  // This probe is done.  Emitted by this class in
  // response to ProbeComplete, after some housekeeping
  void Complete();

 public slots:
  // start execution.  Complete() will be emitted when
  // we're done.  Calls StartProbe().
  void Start();

 public:
  // Has this probe run?  Has it run to completion?
  inline ExecuteStatus execute_status() const { return execute_status_; }

  // Only valid if execute_status() is FINISHED:
  inline ResultStatus  result_status() const { return result_status_; }

  // Get a display string describing the results of this probe.
  virtual QString GetDisplayText() = 0;

  // Retrieve a numeric representation of this fault.
  // 0 is reserved for "success".  Most other values are probe-specific.
  virtual int GetFaultId() = 0;

  // Get a string representation of this probe's class.
  // Can be used with GetFaultId() to construct support urls, though
  // it is not currently used for this.
  virtual QString GetProbeId() = 0;

  // get an HTML blob (possibly large) for inclusion in the report.
  virtual inline QString DebugText() { return log_.GetText(); }

  // Fetch progress made in this probe.
  virtual void GetProgress(int *progress_made, int *total_estimated) = 0;

  // Print debugging information to stderr if debug_ is true. Needs
  // to be a static function as any class might call it.
  static void Debug(const char *fmt, ...);

  // Whether to print debugging information.
  static void PrintDebuggingInformation(bool doit);

 protected:
  // subclasses must implement this as their entry point:
  virtual void StartProbe() = 0;

  void ClearLog() { log_.Clear(); }
  void StartLogDetails();
  void LogDetails(const QString &step, bool good,
                  const QString &details);
  void EndLogDetails();

 private slots:
  // emits Complete() in response to ProbeComplete() after some housekeeping:
  void MarkExecuted(ResultStatus r);

 protected:
  ToStringLog log_;

 private:
  const QString name_;
  ExecuteStatus execute_status_;
  ResultStatus result_status_;
  DISALLOW_EVIL_CONSTRUCTORS(Probe);
  static bool debug_;
};

class ProbeSet : public QObject {
  Q_OBJECT
 public:
  typedef QList<Probe *> Storage;

  explicit ProbeSet(QObject *parent);
  ~ProbeSet();

  // add a probe to the ProbeSet.  This set takes ownership
  // of the pointer and will delete it at the end of the
  // test.
  void AddProbe(Probe *p);

  // get a copy of all the probes.  Valid only until this
  // object is destroyed.
  void GetProbes(Storage *out);

  // rewind the iterator used for NextProbe
  void Rewind();

  // get the next Probe, or NULL if there are no more probes.
  // call Rewind() before calling this the first time.
  Probe *NextProbe();

 private:
  QMutex mutex_;  // protects probes_ and probe_
  Storage probes_;
  Storage::const_iterator next_probe_;
  DISALLOW_EVIL_CONSTRUCTORS(ProbeSet);
};

// Runs the given SLOT on the given object after the event queue empties.
void RunWhenEventQueueEmpties(QObject* object, const char* slot);

}  // namespace popdiag

#endif  // POPDIAG_PROBES_PROBE_H__
